void *allocate(int size, bool exec){
  int prot = PROT_READ | PROT_WRITE | (exec ? PROT_EXEC : 0);
  return mmap(NULL, size, prot, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
}

typedef char *label;

label pc = NULL;
void init_asm(){
  pc = allocate(2<<20, true);
}

typedef enum{badd, bor, badc, bsbb, band, bsub, bxor, bcmp} bop;
const int bmul = 5, bdiv = 7, bmod = 6, bsal = 4, bsar = 7;
typedef enum{cco, ccno, ccc, ccnc, cce, ccne, ccbe, ccnbe, ccs, ccns, ccp, ccnp, ccl, ccge, ccle, ccg} cond;
typedef enum{eax, ecx, edx, ebx, esp, ebp, esi, edi} regist;
typedef enum{di0, di1, di4, dix} displ;
typedef enum{sc1, sc2, sc4, sc8} scale;
typedef struct{regist r0, r1; int s, d;} address;
typedef union{
  struct{
    regist r0 : 3, r1 : 3;
    int d : 2;
  };
  int i;
} modrmsib;

inline void binop(bop b, regist d, regist s){
  modrmsib mod;
  *pc++ = 0x01 | b << 3;
  mod.d = dix;
  mod.r1 = s;
  mod.r0 = d;
  *pc++ = mod.i;
}

inline void bincop(int bcop, regist s){
  modrmsib mod;
  *pc++ = 0xF7;
  mod.d = dix;
  mod.r1 = bcop;
  mod.r0 = s;
  *pc++ = mod.i;
}

inline void binsop(int bsop, regist s){
  modrmsib mod;
  *pc++ = 0xD3;
  mod.d = dix;
  mod.r1 = bsop;
  mod.r0 = s;
  *pc++ = mod.i;
}

inline void nop(){
  *pc++ = 0x90;
}

inline void pushr(regist r){
  *pc++ = 0x50+r;
}

inline void pushimm(int imm){
  if(-128 <= imm && imm < 128){
    *pc++ = 0x6A;
    *pc++ = imm;
  }else{
    *pc++ = 0x68;
    *pc++ = imm & 0xFF;
    *pc++ = (imm >>= 8) & 0xFF;
    *pc++ = (imm >>= 8) & 0xFF;
    *pc++ = (imm >> 8);
  }
}

inline label pushu(){
  label l;
  *pc++ = 0x68;
  l = pc;
  pc += 4;
  return l;
}

inline void popr(regist r){
  *pc++ = 0x58+r;
}

inline void movr(regist d, regist s){
  modrmsib mod;
  *pc++ = 0x89;
  mod.d = dix;
  mod.r1 = s;
  mod.r0 = d;
  *pc++ = mod.i;
}

inline void getd(int dp, regist d){
  modrmsib mod;
  *pc++ = 0x8B;
  mod.d = di0;
  mod.r1 = d;
  mod.r0 = ebp;
  *pc++ = mod.i;
  *pc++ = dp & 0xFF;
  *pc++ = (dp >>= 8) & 0xFF;
  *pc++ = (dp >>= 8) & 0xFF;
  *pc++ = (dp >> 8);
}

inline void setd(int dp, regist d){
  modrmsib mod;
  *pc++ = 0x89;
  mod.d = di0;
  mod.r1 = d;
  mod.r0 = ebp;
  *pc++ = mod.i;
  *pc++ = dp & 0xFF;
  *pc++ = (dp >>= 8) & 0xFF;
  *pc++ = (dp >>= 8) & 0xFF;
  *pc++ = (dp >> 8);
}

inline void addimm(regist d, int imm){
  modrmsib mod;
  mod.d = dix;
  mod.r1 = eax;
  mod.r0 = d;
  if(-128 <= imm && imm < 128){
    *pc++ = 0x83;
    *pc++ = mod.i;
    *pc++ = imm;
  }else{
    *pc++ = 0x81;
    *pc++ = mod.i;
    *pc++ = imm & 0xFF;
    *pc++ = (imm >>= 8) & 0xFF;
    *pc++ = (imm >>= 8) & 0xFF;
    *pc++ = (imm >> 8);
  }
}

inline void callimm(int d){
  *pc++ = 0xE8;
  d -= (int) pc+4;
  *pc++ = d & 0xFF;
  *pc++ = (d >>= 8) & 0xFF;
  *pc++ = (d >>= 8) & 0xFF;
  *pc++ = (d >> 8);
}

inline void ret(){
  *pc++ = 0xC3;
}

inline void cmpr(regist d, regist s){
  modrmsib mod;
  *pc++ = 0x39;
  mod.d = dix;
  mod.r1 = s;
  mod.r0 = d;
  *pc++ = mod.i;
}

inline void cmpimm(regist d, int imm){
  modrmsib mod;
  mod.d = dix;
  mod.r1 = edi;
  mod.r0 = d;
  if(-128 <= imm && imm < 128){
    *pc++ = 0x83;
    *pc++ = mod.i;
    *pc++ = imm;
  }else{
    *pc++ = 0x81;
    *pc++ = mod.i;
    *pc++ = imm & 0xFF;
    *pc++ = (imm >>= 8) & 0xFF;
    *pc++ = (imm >>= 8) & 0xFF;
    *pc++ = (imm >> 8);
  }
}

inline void jmpl(label l){
  int ad = (int) l;
  *pc++ = 0xE9;
  ad -= (int) pc+4;
  *pc++ = ad & 0xFF;
  *pc++ = (ad >>= 8) & 0xFF;
  *pc++ = (ad >>= 8) & 0xFF;
  *pc++ = (ad >> 8);
}

inline label jmpu(){
  label l;
  *pc++ = 0xE9;
  l = pc;
  pc += 4;
  return l;
}

inline void jccl(cond cc, label l){
  int ad = (int) l;
  *pc++ = 0x0F;
  *pc++ = 0x80+cc;
  ad -= (int) pc+4;
  *pc++ = ad & 0xFF;
  *pc++ = (ad >>= 8) & 0xFF;
  *pc++ = (ad >>= 8) & 0xFF;
  *pc++ = (ad >> 8);
}

inline label jccu(cond cc){
  label l;
  *pc++ = 0x0F;
  *pc++ = 0x80+cc;
  l = pc;
  pc += 4;
  return l;
}

inline void setlbl(label l){
  int ad = (int) pc;
  ad -= (int) l+4;
  *l++ = ad & 0xFF;
  *l++ = (ad >>= 8) & 0xFF;
  *l++ = (ad >>= 8) & 0xFF;
  *l = (ad >> 8);
}

inline label getlbl(){
  return pc;
}

inline void addr(int d, regist b, regist x){
  modrmsib mod;
  mod.r1 = x;
  mod.r0 = b;
  if(-128 <= d && d < 128){
    mod.d = di1;
    *pc++ = mod.i;
    *pc++ = d;
  }else{
    mod.d = di4;
    *pc++ = mod.i;
    *pc++ = d & 0xFF;
    *pc++ = (d >>= 8) & 0xFF;
    *pc++ = (d >>= 8) & 0xFF;
    *pc++ = (d >> 8);
  }
}

inline void sibaddr(int d, regist b, regist i, regist x){
  modrmsib mod, sib;
  mod.r1 = x;
  mod.r0 = esp;
  sib.r0 = b;
  sib.r1 = i;
  sib.d = sc4;
  if(-128 <= d && d < 128){
    mod.d = di1;
    *pc++ = mod.i;
    *pc++ = sib.i;
    *pc++ = d;
  }else{
    mod.d = di4;
    *pc++ = mod.i;
    *pc++ = sib.i;
    *pc++ = d & 0xFF;
    *pc++ = (d >>= 8) & 0xFF;
    *pc++ = (d >>= 8) & 0xFF;
    *pc++ = (d >> 8);
  }
}


inline void getlocal(int ix, regist r){
  *pc++ = 0x8B;
  sibaddr(4*ix, esp, esp, r);
}
inline void getattr(regist b, int ix, regist r){
  *pc++ = 0x8B;
  addr(4*ix, b, r);
}
inline void getarray(regist b, regist ix, regist r){
  *pc++ = 0x8B;
  sibaddr(8, b, ix, r);
}


inline void setlocal(int ix, regist r){
  *pc++ = 0x89;
  sibaddr(4*ix, esp, esp, r);
}
inline void setattr(regist b, int ix, regist r){
  *pc++ = 0x89;
  addr(4*ix, b, r);
}
inline void setarray(regist b, regist ix, regist r){
  *pc++ = 0x89;
  sibaddr(8, b, ix, r);
}

inline void setlocalimm(int ix, int imm){
  *pc++ = 0xC7;
  sibaddr(4*ix, esp, esp, eax);
  *pc++ = imm & 0xFF;
  *pc++ = (imm >>= 8) & 0xFF;
  *pc++ = (imm >>= 8) & 0xFF;
  *pc++ = (imm >> 8);
}
inline void setattrimm(regist b, int ix, int imm){
  *pc++ = 0xC7;
  addr(4*ix, b, eax);
  *pc++ = imm & 0xFF;
  *pc++ = (imm >>= 8) & 0xFF;
  *pc++ = (imm >>= 8) & 0xFF;
  *pc++ = (imm >> 8);
}
inline void setarrayimm(regist b, regist ix, int imm){
  *pc++ = 0xC7;
  sibaddr(8, b, ix, eax);
  *pc++ = imm & 0xFF;
  *pc++ = (imm >>= 8) & 0xFF;
  *pc++ = (imm >>= 8) & 0xFF;
  *pc++ = (imm >> 8);
}


inline void pushlocal(int ix){
  *pc++ = 0xFF;
  sibaddr(4*ix, esp, esp, esi);
}
inline void pushattr(regist b, int ix){
  *pc++ = 0xFF;
  addr(4*ix, b, esi);
}
inline void pusharray(regist b, regist ix){
  *pc++ = 0xFF;
  sibaddr(8, b, ix, esi);
}


inline void poplocal(int ix){
  *pc++ = 0x8F;
  sibaddr(4*ix, esp, esp, eax);
}
inline void popattr(regist b, int ix){
  *pc++ = 0x8F;
  addr(4*ix, b, eax);
}
inline void poparray(regist b, regist ix){
  *pc++ = 0x8F;
  sibaddr(8, b, ix, eax);
}


inline void calllocal(int ix){
  *pc++ = 0xFF;
  sibaddr(4*ix, esp, esp, edx);
}
inline void callattr(regist b, int ix){
  *pc++ = 0xFF;
  addr(4*ix, b, edx);
}
inline void callarray(regist b, regist ix){
  *pc++ = 0xFF;
  sibaddr(8, b, ix, edx);
}


inline void cmplocal(int ix, regist r){
  *pc++ = 0x39;
  sibaddr(4*ix, esp, esp, r);
}
inline void cmpattr(regist b, int ix, regist r){
  *pc++ = 0x39;
  addr(4*ix, b, r);
}
inline void cmparray(regist b, regist ix, regist r){
  *pc++ = 0x39;
  sibaddr(8, b, ix, r);
}


inline void cmpaddr(int d, regist b, int imm){
  if(-128 <= imm && imm < 128){
    *pc++ = 0x83;
    addr(d, b, edi);
    *pc++ = imm;
  }else{
    *pc++ = 0x81;
    addr(d, b, edi);
    *pc++ = imm & 0xFF;
    *pc++ = (imm >>= 8) & 0xFF;
    *pc++ = (imm >>= 8) & 0xFF;
    *pc++ = (imm >> 8);
  }
}
inline void cmpsibaddr(int d, regist b, regist i, int imm){
  if(-128 <= imm && imm < 128){
    *pc++ = 0x83;
    sibaddr(d, b, i, edi);
    *pc++ = imm;
  }else{
    *pc++ = 0x81;
    sibaddr(d, b, i, edi);
    *pc++ = imm & 0xFF;
    *pc++ = (imm >>= 8) & 0xFF;
    *pc++ = (imm >>= 8) & 0xFF;
    *pc++ = (imm >> 8);
  }
}
inline void cmplocalimm(int ix, int imm){
  cmpsibaddr(4*ix, esp, esp, imm);
}
inline void cmpattrimm(regist b, int ix, int imm){
  cmpaddr(4*ix, b, imm);
}
inline void cmparrayimm(regist b, regist ix, int imm){
  cmpsibaddr(4*8, b, ix, imm);
}
